const {
    effect,
    ref
} = VueReactivity

const bol = ref(false)

function shouldSetAsProps(el, key, value) {
    // 特殊处理
    if (key === 'form' && el.tagName === 'INPUT') return false
    // 兜底
    return key in el
}

function createRenderer(options) {

    const {
        createElement,
        setElementText,
        insert,
        patchProps
    } = options


    function mountElement(vnode, container) {
        const el = createElement(vnode.type)
        if (typeof vnode.children === 'string') {
            setElementText(el, vnode.children)
        } else if (Array.isArray(vnode.children)) {
            vnode.children.forEach(child => {
                patch(null, child, el)
            })
        }

        if (vnode.props) {
            for (const key in vnode.props) {
                // 调用 patchProps 函数即可
                patchProps(el, key, null, vnode.props[key])
            }
        }

        insert(el, container)
    }

    function patchElement(n1, n2) {
        const el = n2.el = n1.el
        const oldProps = n1.props
        const newProps = n2.props
        // 第一步：更新 props
        for (const key in newProps) {
            if (newProps[key] !== oldProps[key]) {
                patchProps(el, key, oldProps[key], newProps[key])
            }
        }
        for (const key in oldProps) {
            if (!(key in newProps)) {
                patchProps(el, key, oldProps[key], null)
            }
        }

        // 第二步：更新 children
        patchChildren(n1, n2, el)
    }

    function patch(n1, n2, container) {
        if (n1 && n1.type !== n2.type) {
            unmount(n1);
            n1 = null;
        }
        // 代码运行到这里，证明 n1 和 n2 所描述的内容相同
        const {
            type
        } = n2;
        // 如果 n2.type 的值是字符串类型，则它描述的是普通标签素
        if (typeof type === "string") {
            if (!n1) {
                mountElement(n2, container);
            } else {
                patchElement(n1, n2);
            }
        } else if (typeof type === "object") {
            // 如果 n2.type 的值的类型是对象，则它描述的是组件
        } else if (type === "xxx") {
            // 处理其他类型的 vnode
        }
    }

    function unmount(vnode) {
        const parent = vnode.el.parentNode
        if (parent) {
            parent.removeChild(vnode.el)
        }
    }

    function render(vnode, container) {
        if (vnode) {
            patch(container._vnode, vnode, container)
        } else {
            if (container._vnode) {
                // 调用 unmount 函数卸载 vnode
                unmount(container._vnode)
            }
        }
        container._vnode = vnode
    }

    return {
        render
    }
}


// 在创建 renderer 时传入配置项
const renderer = createRenderer({
    createElement(tag) {
        // console.log(`创建元素 ${tag}`)
        const elm = document.createElement(tag)
        return elm
    },
    setElementText(el, text) {
        console.log(`设置 ${JSON.stringify(el)} 的文本内容：${text}`)
        el.textContent = text
    },
    insert(el, parent, anchor = null) {
        // console.log(`将 ${JSON.stringify(el)} 添加到 ${JSON.stringify(parent)} 下`)
        parent.appendChild(el);
    },

    patchProps(el, key, prevValue, nextValue) {
        if (/^on/.test(key)) {
            const invokers = el._vei || (el._vei = {})
            let invoker = invokers[key]
            const name = key.slice(2).toLowerCase()
            if (nextValue) {
                if (!invoker) {
                    invoker = el._vei[key] = (e) => {
                        // e.timeStamp 是事件发生的时间
                        // 如果事件发生的时间早于事件处理函数绑定的时间，则不执行事件处理函数
                        if (e.timeStamp < invoker.attached) return
                        if (Array.isArray(invoker.value)) {
                            invoker.value.forEach(fn => fn(e))
                        } else {
                            invoker.value(e)
                        }
                    }
                    invoker.value = nextValue
                    // 添加 invoker.attached 属性，存储事件处理函数被绑定的时间
                    invoker.attached = performance.now()
                    el.addEventListener(name, invoker)
                } else {
                    invoker.value = nextValue
                }
            } else if (invoker) {
                el.removeEventListener(name, invoker)
            }
        } else if (key === 'class') {
            // 省略部分代码
        } else if (shouldSetAsProps(el, key, nextValue)) {
            // 省略部分代码
        } else {
            // 省略部分代码
        }
    }
})

effect(() => {
    const vnode = {
        type: 'div',
        props: bol.value ? {
            onClick: () => {
                alert('父元素 clicked')
            }
        } : {},
        children: [{
            type: 'p',
            props: {
                onClick: () => {
                    bol.value = true
                }
            },
            children: 'child text'
        }]
    }
    // 渲染 vnode
    renderer.render(vnode, document.querySelector('#app'))
})